//////////////////////////////////////////////////////////////////
// MattiseOS Kernel
// Date: 2008
// Author: Matthew Iselin
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//////////////////////////////////////////////////////////////////

#ifndef PROCESSCOMMON_H
#define PROCESSCOMMON_H

// Includes
#include <core/intrcommon.h>
#include <devman/devman.h>

// Defines

// process states
#define PSTATE_READY        0       // ready to switch to
#define PSTATE_RUNNING      1       // running right now
#define PSTATE_ALREADY      2       // has already had its share
#define PSTATE_DEAD			3		// been killed, should be removed
#define PSTATE_WAITING		4		// the process is waiting for I/O
#define PSTATE_SLEEPING		5		// the process is sleeping
#define PSTATE_WAITPROC		6		// the process is waiting for another process to die
#define PSTATE_INVALID      0xdead  // not a valid task (probably killed) - these *should* be removed

// process creation flags
#define TF_NONE             0       // no flags :(
#define TF_VM86             1       // virtual 8086 mode task
#define TF_USPACE           2       // userspace task
#define TF_KSPACE           4       // kernel space task
#define TF_CTXONLY			8		// create only a context, something else is going to setup stacks and the like

// sizes
#define SZ_KSTACK			2		// kernel stack size
#define SZ_PSTACK			32		// user stack size
#define SZ_BITMAP(stk_sz)	(((0x10000000 / 2) / stk_sz) / (sizeof(uint32_t) * 8))

// stack locations
#define STACK_KERN_VIRT		0x50000000
#define STACK_USER_VIRT		0xD0000000

// memory map element types - any nonzero type is freed but
// this works nicely if we want to print out debug info
#define MEMMAP_ELEM_INVALID	0
#define MEMMAP_ELEM_STACK	1 // a stack
#define MEMMAP_ELEM_BINMEM	2 // the binary in memory
#define MEMMAP_ELEM_HEAP	3 // a heap
#define MEMMAP_ELEM_MAX		3 // maximum possible type

// External Variables

// the kernel process id
extern int kernprocess_parentid;

// Types

// an element in a process memory map
struct ctx_mem_map
{
	uint8_t type;
	uint32_t base;
	uint32_t size; // in BYTES
	struct ctx_mem_map* next;
};

// a process
struct context
{
    // the stack pointer
    uint32_t esp;

    // the page directory
    uint32_t cr3;

	// the value in ebp
    uint32_t ebp;

    // the EIP to return to
    uint32_t eip;

    // status
    uint32_t status;

	// process flags
	uint32_t flags;

    // process id
    uint32_t id;

    // virtual mode task?
    uint8_t vm86task;

    // parent id
    uint32_t parentid;

    // first child id
    uint32_t childid;

    // process or not?
    uint8_t isprocess;

    // first run?
    uint8_t firstrun;

    // the number of children this process has
    uint32_t numchildren;

	// the PIDs of children
	uint32_t* childpids;

    // this process timeslice
    uint32_t timeslice;

    // the process priority
    uint32_t priority;

    // sleeping setup
    uint32_t sleep_start_time;
    uint32_t sleep_end_time;
    uint32_t sleep_curr_time;

    // the heap information (virtual - so they appear to be contiguous)
    uint32_t heap_base;
    uint32_t heap_curr;

    // argument information (location and count)
    uint32_t argv;
    int32_t argc;

    // environment information
    uint32_t env;

    // wait for process information
    uint32_t waitid;

	// the virtual address of the user stack
	uint32_t userstack;

    // the virtual address of the bottom of our (kernel) stack
    uint32_t kernstack;
    uint32_t kernstack_sz;

    // the physical address of the bottom of our (kernel) stack
    uint32_t kernstack_phys;

    // the process current directory (for prepending onto device i/o opens)
    char currdir[1024];

	// a pointer to the name of an executable to load and execute
	// set to NULL to basically avoid loading a binary
	char* exe_name;

    // file descriptors for this task
    struct fd filedescs[512];

	// total stack space = 0xe0000000 - 0xf0000000 (256 MB)
	// so 0x10000000 / SZ_STACK is how many stack we can store
	// 32 stacks therefore per uint32_t
	// each stack is a bit, so: num_stacks / ( sizeof( <type> ) * 4 )
	uint32_t *kernel_stack_bitmap, *user_stack_bitmap;

	// the parent context for this task
	struct context* parent;

	// the pentry pointer for this task
	struct pentry* ent;

	// a pointer to a context to signal to awaken when we get killed
	struct context* wake_on_die;

	// memory map for the process - basically tells about freeable
	// regions
	struct ctx_mem_map* memmap;
} __attribute__((packed));

// an entry in the process table
struct pentry
{
    // the context before this one
    struct pentry* prev;

    // the context that this entry uses
    struct context* me;

    // the next context
    struct pentry* next;
} __attribute__((packed));

// Prototypes

// the last allocated pid
extern uint32_t lastpid;

// the current process
extern struct context* currprocess;

// the process table
extern struct pentry* ptable;

// rather than having a process table, i'm trying something new
extern struct pentry* p_readyq; // ready queue
extern struct pentry* p_alreadyq; // swapped with ready when ready becomes empty
extern struct pentry* p_deadq; // dead queue (for removal)
extern struct pentry* p_waitq; // waiting queue - will need to write some extra code
extern struct pentry* p_sleepq; // sleep queue (advantage is that if no processes are sleeping IRQ0 is faster)

// sets up multitasking
void kInitTasking();

// selects a task to execute and switches contexts
void kSchedule( uint32_t );

// creates a new process
int32_t kCreateProcess( uint32_t startaddr, uint32_t flags, uint32_t pagedir, uint32_t prio, uint32_t* childid, char** argv );

// creates a new process that must run an executable file rather than a specific address
int32_t kCreateProcessFromEXE( const char* exename, uint32_t flags, uint32_t pagedir, uint32_t prio, uint32_t* childid, char** argv );

// creates a new thread
int32_t kCreateThread( uint32_t parent, uint32_t startaddr, uint32_t flags, uint32_t prio, char** argv, struct pentry* parent_ctx, const char* exename );

// kills a thread
void kill( uint32_t pid );

// cleans up
void kCleanUpScheduler();

// sleeps for a certain number of seconds
uint32_t sleep( uint32_t seconds );

// sleeps for a certain number of milliseconds
uint32_t delay( uint32_t t );

// gets the current thread id
uint32_t getpid();

// forks a process (ie, creates a process with the exact same setup in a new address space)
uint32_t fork();

// Puts a process onto the wait queue, where it can only be woken
// by another process signalling it. Blocks until woken.
void wait();

// Signals a process to wake up.
void wake(struct context* proc);

// sets up the kernel stack allocator
void setup_kernel_stack_heap( uint32_t maxmem );

// sets up the stack bitmaps for a context
void SetUpContextBitmap(struct context* ctx);

// returns a stack to a child thread
void ReturnChildKernelStack(uint32_t stack);

// returns a user stack for a child thread
void ReturnChildUserStack(struct context* ctx, uint32_t stack);

// gets a stack to use for a new child thread
uint32_t GetChildKernelStack();

// gets a stack to use for a new child thread
uint32_t GetChildUserStack(struct context* ctx);

#endif
